package egar

import (
	"bytes"
	"encoding/binary"
	"math"
)

type Buffer struct {
	data        []byte
	readOffset  int
	writeOffset int
}

func NewBuffer(capacity int) *Buffer {
	return &Buffer{
		data:        make([]byte, capacity),
		readOffset:  0,
		writeOffset: 0,
	}
}

func NewBufferFormData(data []byte) *Buffer {
	return &Buffer{
		data:        data,
		readOffset:  0,
		writeOffset: len(data),
	}
}

func (b *Buffer) GetData() []byte {
	return b.data
}

func (b *Buffer) Clear() {
	b.readOffset = 0
	b.writeOffset = 0
}

func (b *Buffer) ReadableBytes() int {
	return b.writeOffset - b.readOffset
}

func (b *Buffer) WriteableBytes() int {
	return len(b.data) - b.writeOffset
}

/*
LittleEndian: 高位存储在高地址，低位存储在低地址
BigEndian: 高位存储在低地址，低位存储在高地址
value is big-endian mode
*/
func (b *Buffer) WriteBytes(value []byte, LE bool) {
	size := len(value)
	if b.WriteableBytes() < size {
		panic("write bytes out of range")
	}
	if LE {
		for i := 0; i < size; i++ {
			b.data[b.writeOffset+i] = value[size-i-1]
		}
	} else {
		copy(b.data[b.writeOffset:], value)
	}
	b.writeOffset += size
}

func (b *Buffer) WriteUint8(value uint8) {
	b.WriteBytes([]byte{value}, true)
}

func (b *Buffer) WriteInt8(value int8) {
	b.WriteUint8(uint8(value))
}

func (b *Buffer) WriteUint16(value uint16, LE bool) {
	buffer := bytes.NewBuffer(make([]byte, 0, 2))
	binary.Write(buffer, binary.BigEndian, value)
	b.WriteBytes(buffer.Bytes(), LE)
}

func (b *Buffer) WriteInt16(value int16, LE bool) {
	b.WriteUint16(uint16(value), LE)
}

func (b *Buffer) WriteUint32(value uint32, LE bool) {
	buffer := bytes.NewBuffer(make([]byte, 0, 4))
	binary.Write(buffer, binary.BigEndian, value)
	b.WriteBytes(buffer.Bytes(), LE)
}

func (b *Buffer) WriteInt32(value int32, LE bool) {
	b.WriteUint32(uint32(value), LE)
}

func (b *Buffer) WriteUint64(value uint64, LE bool) {
	buffer := bytes.NewBuffer(make([]byte, 0, 8))
	binary.Write(buffer, binary.BigEndian, value)
	b.WriteBytes(buffer.Bytes(), LE)
}

func (b *Buffer) WriteInt64(value int64, LE bool) {
	b.WriteUint64(uint64(value), LE)
}

func (b *Buffer) WriteFloat32(value float32, LE bool) {
	buffer := bytes.NewBuffer(make([]byte, 0, 4))
	binary.Write(buffer, binary.BigEndian, value)
	b.WriteBytes(buffer.Bytes(), LE)
}

func (b *Buffer) WriteFloat64(value float64, LE bool) {
	buffer := bytes.NewBuffer(make([]byte, 0, 8))
	binary.Write(buffer, binary.BigEndian, value)
	b.WriteBytes(buffer.Bytes(), LE)
}

func (b *Buffer) ReadBytes(size int, LE bool) []byte {
	if b.ReadableBytes() < size {
		panic("read bytes out of range")
	}
	result := make([]byte, size)
	if LE {
		for i := 0; i < size; i++ {
			result[size-i-1] = b.data[b.readOffset+i]
		}
	} else {
		copy(result, b.data[b.readOffset:b.readOffset+size])
	}
	b.readOffset += size
	return result
}

func (b *Buffer) ReadUint8() uint8 {
	return b.ReadBytes(1, true)[0]
}

func (b *Buffer) ReadInt8() int8 {
	return int8(b.ReadUint8())
}

func (b *Buffer) ReadUint16(LE bool) uint16 {
	return binary.BigEndian.Uint16(b.ReadBytes(2, LE))
}

func (b *Buffer) ReadInt16(LE bool) int16 {
	return int16(b.ReadUint16(LE))
}

func (b *Buffer) ReadUint32(LE bool) uint32 {
	return binary.BigEndian.Uint32(b.ReadBytes(4, LE))
}

func (b *Buffer) ReadInt32(LE bool) int32 {
	return int32(b.ReadUint32(LE))
}

func (b *Buffer) ReadUint64(LE bool) uint64 {
	return binary.BigEndian.Uint64(b.ReadBytes(8, LE))
}

func (b *Buffer) ReadInt64(LE bool) int64 {
	return int64(b.ReadUint64(LE))
}

func (b *Buffer) ReadFloat32(LE bool) float32 {
	return math.Float32frombits(b.ReadUint32(LE))
}

func (b *Buffer) ReadFloat64(LE bool) float64 {
	return math.Float64frombits(b.ReadUint64(LE))
}
